package deprecated;

import static base.Edge.TURN_OFF;
import static base.Edge.TURN_ON;

import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;

import base.Edge;
import base.FlexibleComponent;

import com.google.common.collect.ForwardingSetMultimap;
import com.google.common.collect.ImmutableSet;
import com.google.common.collect.SetMultimap;
import com.google.common.primitives.Ints;

/**
 * BankerOrder is a {@link Comparator} to sort solutions by their size: the number of
 * fixed edges they contain.  The class also has a factory to provide splitting instances for
 * Collections that sort via BankerOrder.
 * <p>
 * To be consistent with equals some other sorting criteria are used; objects with more
 * red edges are less than objects with more green edges, and the lowest of the first
 * mismatching targets is lower.
 * <p>
 * NB: The arguments to the BankerOrder comparator must contain only {@link Edge#TURN_ON} or
 * {@link Edge#TURN_OFF} keys for the comparator to function properly.  Overlapping value sets,
 * while garbage, will be sorted without raising any alarms.
 * <p>
 * Finally, the splitting instances will not perform correctly if any of the objects in a
 * sort contain invalid values.
 * @deprecated {@code BankerOrder} was intended to sort the enumerated components, as represented by a {@link SetMultimap}.
 * That representation has been superseded by {@link FlexibleComponent}, which implements {@link Comparable} and it's own
 * version of {@link #markerSet(int)}.
 */
@Deprecated
public enum BankerOrder implements Comparator<SetMultimap<Edge, Integer>> {
	INSTANCE;

	@Override
	public int compare(SetMultimap<Edge, Integer> o1, SetMultimap<Edge, Integer> o2) {
		
		if (o1 == o2 || (o1.isEmpty() && o2.isEmpty())) return 0;
		
		int result = o1.size() - o2.size();
		if (result!=0) return result; // smaller map left, larger map right
		
		assert o1.size() == o2.size();
		
		result = o1.get(TURN_OFF).size() - o2.get(TURN_OFF).size();	//smaller red left, larger red right						
		if (result!=0) return result;

		assert o1.get(TURN_OFF).size() == o2.get(TURN_OFF).size() && o1.get(TURN_ON).size() == o2.get(TURN_ON).size(); 
		
		if (!o1.get(TURN_OFF).isEmpty()) {
			Iterator<Integer> o1Iter = o1.get(TURN_OFF).iterator();
			Iterator<Integer> o2Iter = o2.get(TURN_OFF).iterator();
			while (o1Iter.hasNext() && o2Iter.hasNext()) 
				if ((result = Ints.compare(o1Iter.next(),o2Iter.next()))!=0) //smaller first different red element left, larger first different red element right
					return result;
		}
			
		assert o1.get(TURN_OFF).equals(o2.get(TURN_OFF));
		
		Iterator<Integer> o1Iter = o1.get(TURN_ON).iterator();
		Iterator<Integer> o2Iter = o2.get(TURN_ON).iterator();
		while (o1Iter.hasNext() && o2Iter.hasNext()) 
			if ((result = Ints.compare(o1Iter.next(),o2Iter.next()))!=0) //smaller first different green element left, larger first different green element right
				return result;

		assert o1.get(TURN_ON).equals(o2.get(TURN_ON)) && result == 0;
		
		return result;
	}

	//TODO some sort of cache?
	
	/**
	 * A set for splitting {@link SetMultimap}s with {@link BankerOrder#INSTANCE} as their
	 * comparator.
	 * <p>
	 * A marker setmultimap is otherwise non-functional.  Attempts to use it any other context will
	 * probably throw a {@link NullPointerException}, though that is not guaranteed.
	 * @param dropSize the size of setmultimaps that come before the marker setmultimap -
	 * i.e., the set resulting from tail(markerSet(dropSize)) will contain sets only larger than dropSize.
	 * @return
	 */
	public static SetMultimap<Edge,Integer> markerSet(final int dropSize) {
		return new MarkerSetMultimap(dropSize+1);
	}
	
	static class MarkerSetMultimap extends ForwardingSetMultimap<Edge,Integer> {
		private final int size;
		private final static ImmutableSet<Integer> turn_on = ImmutableSet.of(-1);
		MarkerSetMultimap(int size) { this.size = size; }
		@Override protected SetMultimap<Edge,Integer> delegate() { return null; }
		@Override public boolean isEmpty() { return false; }
		@Override public int size() { return size; }
		@Override public Set<Integer> get(Edge edge) { 
			switch (edge) {
			case TURN_ON : return turn_on;
			default : return ImmutableSet.of();
			}
		}
	}
	
}
